/*
 * Copyright (C) 2008 CERNET Network Center 
 * dvping/dvmcast/hdvping/hdvmcast series 
 * 
 * Design and coding: 
 *  	Xing Li <xing@cernet.edu.cn> 
 *	Congxiao Bao <congxiao@cernet.edu.cn>
 * 	Jinpeng Jiang <jjp02@mails.tsinghua.edu.cn>
 * 
 * Contributions:
 * 
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 */


#include <DateTime.h>
#include "dvpacketqueue.h"
#include "rtp.h"
#include "Constants.h"

DVPacketQueue::DVPacketQueue()
{
	pkt_lock.Init();
	free_pkt_lock.Init();
	packet_count = 0;
}
DVPacketQueue::~DVPacketQueue()
{
	pkt_lock.Destroy();
	free_pkt_lock.Destroy();
}
bool DVPacketQueue::Init()
{
	packet_iterator it = packets.begin();
	for(; it != packets.end(); ++it)
	{
		if(it->pdata != NULL)
		{
			delete [] it->pdata;
		}
	}
	packets.resize(0);

	for(u_int32_t i = 0; i < free_packets.size(); ++i)
	{
		if(free_packets[i].pdata != NULL)
		{
			delete [] free_packets[i].pdata;
		}
	}
	free_packets.resize(0);

	DVPacket head;
	head.length = 0;
	head.pdata = NULL;
	head.ref_count = 0;
	packets.push_back(head);
	packet_count = 0;
	return true;
}
bool DVPacketQueue::Cleanup()
{
	packet_iterator it = packets.begin();
	for(; it != packets.end(); ++it)
	{
		if(it->pdata != NULL)
		{
			delete [] it->pdata;
		}
	}
	packets.resize(0);

	for(u_int32_t i = 0; i < free_packets.size(); ++i)
	{
		if(free_packets[i].pdata != NULL)
		{
			delete [] free_packets[i].pdata;
		}
	}
	free_packets.resize(0);
	return true;
}
bool DVPacketQueue::AddPacket(DVPacket pkt)
{
	int64_t start = DateTime::Now().GetTickCount();
	pkt_lock.Lock();
	pkt.ref_count = 0;
	packets.push_back(pkt);
	++ packet_count;
	pkt_lock.Unlock();
	int64_t end = DateTime::Now().GetTickCount();
	if(end - start > 1000)
	{
//		cout << "add packet:" << end - start << endl;
	}
	return true;
}
packet_iterator DVPacketQueue::GetFirstPacket()
{
	pkt_lock.Lock();
	packet_iterator it = packets.begin();
	pkt_lock.Unlock();
	return it;
}
char* DVPacketQueue::GetNextPacket(packet_iterator& it, u_int32_t& length, IPEndPoint& ep, u_int32_t& pkt_left)
{
	int64_t start = DateTime::Now().GetTickCount();

	char* pdata = NULL;
	pkt_lock.Lock();
	packet_iterator next = it;
	++next;
	if(next == packets.end())
	{
		pdata = NULL;
		length = 0;
	}
	else
	{
		if(it != packets.begin())
		{
            --(it->ref_count);
			packet_iterator second = packets.begin();
			++second;
			if(it->ref_count == 0 && it == second)
			{
				if(it->pdata != NULL)
				{
					free_pkt_lock.Lock();
					free_packets.push_back(*it);
					free_pkt_lock.Unlock();
				}
				packets.erase(it);
				--packet_count;
			}
		}
		it = next;
		pdata = it->pdata;
		length = it->length;
		ep = it->srcep;
		++(it->ref_count);	
	}
	pkt_left = packet_count;
	pkt_lock.Unlock();
	int64_t end = DateTime::Now().GetTickCount();
	if(end - start > 1000)
	{
//		cout << "get next packet:" << end - start << endl;
	}
	return pdata;
}
bool DVPacketQueue::ReleaseIterator(packet_iterator it)
{
	pkt_lock.Lock();
	assert(it != packets.end());
	if(it != packets.begin())
	{
        --(it->ref_count);
		packet_iterator second = packets.begin();
		++second;
		if(it->ref_count == 0 && it == second)
		{
			++it;
			free_pkt_lock.Lock();
			free_packets.push_back(*second);
			packets.erase(second);
			--packet_count;
			while(it != packets.end())
			{
				if(it->ref_count == 0)
				{
					second = it;
					++it;
					free_packets.push_back(*second);
					packets.erase(second);
					--packet_count;
				}
				else
					break;
			}
			free_pkt_lock.Unlock();
		}
	}
	pkt_lock.Unlock();
	return true;
}
u_int32_t DVPacketQueue::GetPacketCount()
{
	return packet_count;
}
void DVPacketQueue::GetFreePacket(DVPacket& pkt)
{
	free_pkt_lock.Lock();
	if(free_packets.empty())
	{
		pkt.pdata = new char[sizeof(rtp_hdr_t) + DIF_BLOCK_SIZE * DIF_BLOCK_PER_PACKET];
	}
	else
	{
		pkt.pdata = free_packets.back().pdata;
		free_packets.pop_back();
	}
	free_pkt_lock.Unlock();
	return;
}
